﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LeeAnn.Framework;
using System.Threading;

namespace LeeAnn
{
    public class Neuron
    {
        public Position3D Position { get; set; }
        public List<NeuronPlaceholder> Inputs;
        public List<NeuronPlaceholder> Outputs;
        private double OutputData;
        public delegate void CalculateIODelegate(List<double> inputs, List<double> inputweights,ref double outputData, AnnFramework frame);
        public delegate void DataDelegate(double[] data);
        public delegate void OutputDelegate(double data);
        public event CalculateIODelegate CalculateEvent;
        public event OutputDelegate OutputEvent;
        public event DataDelegate InputEvent;

        public Boolean HasInputs
        {
            get
            {
                if (input.Count == 0)
                    return false;
                return true;
            }
        }

        private List<double> input;
        public List<double> Weights;

        public Neuron()
        {
            Position = new Position3D(-1, -1, -1);
            Inputs = new List<NeuronPlaceholder>();
            Outputs = new List<NeuronPlaceholder>();
            OutputData = 0;
            input = new List<double>();
            Weights = new List<double>();
        }
        public void RandomizeWeights()
        {
            for (int i = 0; i < Weights.Count; i++)
            {
                Weights[i] = Maths.RandomClamped(-1, 1);
            }
        }
        public void Input(double[] data)
        {
            foreach(double d in data)
                input.Add(d);
        }
        public void Input(double data)
        {
            input.Add(data);
        }
        public void Push(AnnFramework frame)
        {
            int a = 1;
            if(Outputs.Count == 0)
                a = 0;
            int isize = input.Count;
            //Get the hell out of here if we have no inputs.
            if (isize == 0)
                return;
            int wsize = Weights.Count;
            //If there arn't enough weights to correlate to the inputs, create more random weights.
            if (isize > wsize)
            {
                for (int i = 0; i < isize - wsize; i++)
                {
                    //Adds a random weights between -1 and 1;
                    Weights.Add(Maths.RandomClamped(-1, 1));
                    //Thread.Sleep(100);
                }
            }
            //We have an input
            if(InputEvent != null)
                InputEvent.Invoke(input.ToArray());
            //Reset OutputData
            OutputData = 0;
            for (int i = 0; i < input.Count; i++)
                OutputData += input[i];
            //Calculate the inputs
            if(CalculateEvent != null)
                CalculateEvent.Invoke(input, Weights,ref OutputData,frame);
            //Push our output values to our output neurons
            foreach (NeuronPlaceholder np in Outputs)
            {
                //Get the neuron that the placeholder holds
                int n = np.getNeuron(frame);
                //Add inputs to the stack
                if (np.nType == NeuronPlaceholder.NeuronType.Input || np.nType == NeuronPlaceholder.NeuronType.Output)
                    frame.InputOutputNeurons[n].Input(OutputData);
                else
                    frame.Neurons[n].Input(OutputData);
            }
            //Output the outputs
            if (OutputEvent != null)
                OutputEvent.Invoke(OutputData);
            //Clear the inputs
            input = new List<double>();
        }

        public void AddOutput(NeuronPlaceholder output)
        {
            Outputs.Add(output);
        }
        public void AddCalculateEvent(CalculateIODelegate calc)
        {
            if(calc != null)
                CalculateEvent += calc; 
        }
        public void AddOutputEvent(OutputDelegate od)
        {
            if (od != null)
                OutputEvent += od;
        }
        public void AddInputEvent(DataDelegate od)
        {
            if (od != null)
                InputEvent += od;
        }
    }
}
